package com.carb.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.carb.constant.MessageConstant;
import com.carb.dto.*;
import com.carb.entity.Item;
import com.carb.entity.Project;
import com.carb.entity.ProjectItem;
import com.carb.entity.ProjectType;
import com.carb.enums.CommonDeleted;
import com.carb.enums.CommonStatus;
import com.carb.exception.BusinessErrorException;
import com.carb.exception.DeletionNotAllowedException;
import com.carb.exception.EntityNotFoundException;
import com.carb.exception.UpdateStatusFailedException;
import com.carb.mapper.ItemMapper;
import com.carb.mapper.ProjectItemMapper;
import com.carb.mapper.ProjectMapper;
import com.carb.mapper.ProjectTypeMapper;
import com.carb.query.PageQueryUtils;
import com.carb.result.PageResult;
import com.carb.service.IProjectService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.carb.vo.ProjectListVO;
import com.carb.vo.ProjectVO;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务项目管理 服务实现类
 * </p>
 *
 * @author XuChaoyang
 * @since 2025-01-18
 */
@Service
public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> implements IProjectService {

    @Resource
    private ProjectItemMapper projectItemMapper;

    @Resource
    private ProjectTypeMapper projectTypeMapper;

    @Resource
    private ItemMapper itemMapper;

    @Resource
    private ProjectMapper projectMapper;

    /**
     * 新增服务项目
     * @param projectAddDTO 新增服务项目信息
     */
    @Override
    @Transactional
    public void addProject(ProjectAddDTO projectAddDTO) {
        // 1、检查服务项目类型是否存在
        checkProjectTypeExist(projectAddDTO.getProjectTypeId());

        // 2、检查服务项目是否已经存在且逻辑删除（已删除）
        Project isProjectExist = this.getOne(Wrappers.lambdaQuery(Project.class)
                .eq(Project::getName, projectAddDTO.getName())
                .eq(Project::getDeleted, CommonDeleted.DELETED));
        if (isProjectExist != null) {
            // 服务项目id
            Long projectId = isProjectExist.getId();
            // 更新服务项目逻辑删除为未删除
            BeanUtils.copyProperties(projectAddDTO, isProjectExist);
            isProjectExist.setDeleted(CommonDeleted.NOT_DELETED)
                    .setCreateTime(LocalDateTime.now())
                    .setUpdateTime(LocalDateTime.now());
            this.update(isProjectExist, Wrappers.lambdaUpdate(Project.class)
                    .eq(Project::getId, projectId));
            // 保存服务项目物品关联信息
            List<ProjectItemAddDTO> itemList = projectAddDTO.getProjectItems();
            if (itemList.isEmpty()) {
                // 不允许物品为空
                throw new BusinessErrorException(MessageConstant.PROJECT_ITEM_IS_NULL + MessageConstant.PLEASE_CHECK_BEFORE_ADD);
            }
            projectItemMapper.insert(getProjectItemList(itemList, projectId));
            // 跳过新增逻辑
            return;
        }

        // 3、对象属性拷贝
        Project project = new Project();
        BeanUtils.copyProperties(projectAddDTO, project);

        // 4、填充服务项目属性
        // 4.1、设置默认服务项目状态，默认为启用状态
        project.setStatus(CommonStatus.ENABLED);

        // 5、新增服务项目，并得到服务项目id（save后主键会自动set到实体的id字段，只需要getId()就好）
        this.save(project);

        // 6、保存服务项目物品关联信息
        List<ProjectItemAddDTO> itemList = projectAddDTO.getProjectItems();
        if (itemList.isEmpty()) {
            // 不允许物品为空
            throw new BusinessErrorException(MessageConstant.PROJECT_ITEM_IS_NULL + MessageConstant.PLEASE_CHECK_BEFORE_ADD);
        }
        projectItemMapper.insert(getProjectItemList(itemList, project.getId()));
    }

    /**
     * 服务项目分页查询
     * @param projectPageQueryDTO 查询条件
     * @return 服务项目分页结果
     */
    @Override
    public PageResult<ProjectVO> pageQuery(ProjectPageQueryDTO projectPageQueryDTO) {
        // 1、获取查询属性
        // 1.1、分页属性
        Long pageNum = PageQueryUtils.checkPageNum(projectPageQueryDTO.getPageNum());
        Long pageSize = PageQueryUtils.checkPageSize(projectPageQueryDTO.getPageSize());
        String orderBy = projectPageQueryDTO.getOrderBy();
        Boolean isAsc = projectPageQueryDTO.getIsAsc();
        // 1.2、查询属性
        String name = projectPageQueryDTO.getName();
        Long projectTypeId = projectPageQueryDTO.getProjectTypeId();
        CommonStatus status = projectPageQueryDTO.getStatus();

        // 2、构建条件
        // 2.1、分页条件
        Page<Project> page = Page.of(pageNum, pageSize);
        // 2.2、排序条件
        PageQueryUtils.applySorting(page, orderBy, isAsc);

        // 3、服务项目表查询（服务项目名称（非空）、服务项目类型（非空）、服务项目状态（非空）、逻辑删除（未删除））
        Page<Project> projectPage = this.page(page, Wrappers.lambdaQuery(Project.class)
                .like(name != null, Project::getName, name)
                .eq(projectTypeId != null, Project::getProjectTypeId, projectTypeId)
                .eq(status != null, Project::getStatus, status)
                .eq(Project::getDeleted, CommonDeleted.NOT_DELETED));

        // 4、封装VO数据列表
        List<ProjectVO> projectVOList = new ArrayList<>();
        for (Project project : projectPage.getRecords()) {
            ProjectVO projectVO = new ProjectVO();
            BeanUtils.copyProperties(project, projectVO);
            // 服务项目物品关联信息
            List<ProjectItem> projectItemList = projectItemMapper.selectList(Wrappers.lambdaQuery(ProjectItem.class)
                    .eq(ProjectItem::getProjectId, project.getId()));
            projectVO.setProjectItems(projectItemList);
            // 项目类型名称
            String projectTypeName = projectTypeMapper.selectById(project.getProjectTypeId()).getName();
            projectVO.setProjectTypeName(projectTypeName);
            projectVOList.add(projectVO);
        }

        // 5、将实体分页对象转换为VO分页对象
        Page<ProjectVO> projectVOPage = PageQueryUtils.convertToVOPage(projectPage, projectVOList);

        // 6、返回封装结果
        return new PageResult<>(projectVOPage.getTotal(), projectVOPage.getPages(), projectVOPage.getRecords());
    }

    /**
     * 启用/禁用服务项目状态
     * @param status 服务项目状态
     * @param id 服务项目id
     */
    @Override
    @Transactional
    public void updateStatus(Long id, CommonStatus status) {
        // 1、检查服务项目是否存在
        Project project = checkProjectExist(id);

        // 2、检查服务项目类型是否存在、是否禁用
        ProjectType projectType = checkProjectTypeExist(project.getProjectTypeId());
        if (projectType.getStatus().equals(CommonStatus.DISABLED)) {
            // 服务项目类型已禁用，状态更新失败
            throw new UpdateStatusFailedException(MessageConstant.PROJECT_TYPE_DISABLED + MessageConstant.STATUS_UPDATE_FAILED);
        }

        // 3、检查服务项目对应的所有物品是否存在被禁用
        List<Long> itemIds = projectItemMapper.selectList(Wrappers.lambdaQuery(ProjectItem.class)
                .eq(ProjectItem::getProjectId, id)).stream().map(ProjectItem::getItemId).toList();
        if (!itemIds.isEmpty()) {
            List<Item> itemList = itemMapper.selectList(Wrappers.lambdaQuery(Item.class)
                    .in(Item::getId, itemIds));
            if (itemList.stream().anyMatch(item -> item.getStatus().equals(CommonStatus.DISABLED))) {
                // 项目对应的所有物品中存在被禁用的，状态更新失败
                throw new UpdateStatusFailedException(MessageConstant.PROJECT_ITEM_DISABLED + MessageConstant.STATUS_UPDATE_FAILED);
            }
        }

        // 4、更新
        this.update(new Project(), Wrappers.lambdaUpdate(Project.class)
                .eq(Project::getId, id)
                .set(Project::getStatus, status));
    }

    /**
     * 根据服务项目id查询服务项目信息
     * @param id 服务项目id
     * @return 查询结果
     */
    @Override
    public ProjectVO getProjectById(Long id) {
        // 1、检查服务项目是否存在
        Project project = checkProjectExist(id);

        // 2、封装VO
        ProjectVO projectVO = new ProjectVO();
        BeanUtils.copyProperties(project, projectVO);
        // 服务项目物品关联信息
        List<ProjectItem> projectItemList = projectItemMapper.selectList(Wrappers.lambdaQuery(ProjectItem.class)
                .eq(ProjectItem::getProjectId, id));
        projectVO.setProjectItems(projectItemList);
        // 项目类型名称
        String projectTypeName = projectTypeMapper.selectById(project.getProjectTypeId()).getName();
        projectVO.setProjectTypeName(projectTypeName);

        // 3、返回VO
        return projectVO;
    }

    /**
     * 修改服务项目信息
     * @param projectUpdateDTO 修改的服务项目信息
     */
    @Override
    @Transactional
    public void updateProject(ProjectUpdateDTO projectUpdateDTO) {
        // 1、检查服务项目是否存在
        Long projectId = projectUpdateDTO.getId();
        checkProjectExist(projectId);

        // 2、检查服务项目类型是否存在
        checkProjectTypeExist(projectUpdateDTO.getProjectTypeId());

        // 3、服务项目更新
        Project project = new Project();
        BeanUtils.copyProperties(projectUpdateDTO, project);
        project.setUpdateTime(LocalDateTime.now());
        this.update(project, Wrappers.lambdaUpdate(Project.class)
                .eq(Project::getId, projectId));

        // 4、服务项目物品关联信息更新
        // 4.1、删除服务项目物品关联信息
        projectItemMapper.delete(Wrappers.lambdaQuery(ProjectItem.class)
                .eq(ProjectItem::getProjectId, projectId));
        // 4.2、批量插入服务项目物品关联信息
        List<ProjectItemAddDTO> itemList = projectUpdateDTO.getProjectItems();
        if (itemList.isEmpty()) {
            // 不允许物品为空
            throw new BusinessErrorException(MessageConstant.PROJECT_ITEM_IS_NULL + MessageConstant.PLEASE_CHECK_BEFORE_ADD);
        }
        projectItemMapper.insert(getProjectItemList(itemList, projectId));
    }

    /**
     * 删除服务项目（逻辑删除）
     * @param id 服务项目id
     */
    @Override
    @Transactional
    public void deleteProject(Long id) {
        // 1、检查服务项目是否存在
        checkProjectExist(id);

        // 2、删除服务项目物品关联信息
        projectItemMapper.delete(Wrappers.lambdaQuery(ProjectItem.class)
                .eq(ProjectItem::getProjectId, id));

        // 3、删除服务项目（逻辑删除）
        this.update(new Project(), Wrappers.lambdaUpdate(Project.class)
                .eq(Project::getId, id)
                .set(Project::getDeleted, CommonDeleted.DELETED));
    }

    /**
     * 新增服务项目类型
     * @param projectTypeName 新增服务项目类型名称
     */
    @Override
    @Transactional
    public void addProjectType(String projectTypeName) {
        // 1、检查服务项目类型是否已经存在且逻辑删除（已删除）
        ProjectType isProjectTypeExist = projectTypeMapper.selectOne(Wrappers.lambdaQuery(ProjectType.class)
                .eq(ProjectType::getName, projectTypeName)
                .eq(ProjectType::getDeleted, CommonDeleted.DELETED));
        if (isProjectTypeExist != null) {
            // 服务项目类型id
            Long projectTypeId = isProjectTypeExist.getId();
            // 更新服务项目类型逻辑删除为未删除
            isProjectTypeExist.setDeleted(CommonDeleted.NOT_DELETED)
                            .setCreateTime(LocalDateTime.now())
                            .setUpdateTime(LocalDateTime.now());
            projectTypeMapper.update(isProjectTypeExist, Wrappers.lambdaUpdate(ProjectType.class)
                    .eq(ProjectType::getId, projectTypeId));
            // 跳过新增逻辑
            return;
        }

        // 2、新增服务项目类型
        projectTypeMapper.insert(new ProjectType()
                .setName(projectTypeName)
                .setStatus(CommonStatus.ENABLED));
    }

    /**
     * 服务项目类型分页查询
     * @param projectTypePageQueryDTO 查询条件
     * @return 服务项目类型分页结果
     */
    @Override
    public PageResult<ProjectType> pageQueryProjectType(ProjectTypePageQueryDTO projectTypePageQueryDTO) {
        // 1、获取查询属性
        // 1.1、分页属性
        Long pageNum = PageQueryUtils.checkPageNum(projectTypePageQueryDTO.getPageNum());
        Long pageSize = PageQueryUtils.checkPageSize(projectTypePageQueryDTO.getPageSize());
        String orderBy = projectTypePageQueryDTO.getOrderBy();
        Boolean isAsc = projectTypePageQueryDTO.getIsAsc();
        // 1.2、查询属性
        String projectTypeName = projectTypePageQueryDTO.getProjectTypeName();
        CommonStatus status = projectTypePageQueryDTO.getStatus();

        // 2、构造条件
        // 2.1、分页条件
        Page<ProjectType> page = Page.of(pageNum, pageSize);
        // 2.2、排序条件
        PageQueryUtils.applySorting(page, orderBy, isAsc);

        // 3、服务项目类型表查询
        Page<ProjectType> projectTypePage = projectTypeMapper.selectPage(page, Wrappers.lambdaQuery(ProjectType.class)
                .like(projectTypeName != null, ProjectType::getName, projectTypeName)
                .eq(status != null, ProjectType::getStatus, status)
                .eq(ProjectType::getDeleted, CommonDeleted.NOT_DELETED));

        // 4、返回封装结果
        return new PageResult<>(projectTypePage.getTotal(), projectTypePage.getPages(), projectTypePage.getRecords());
    }

    /**
     * 启用/禁用服务项目类型状态
     * @param status 服务项目类型状态
     * @param id 服务项目类型id
     */
    @Override
    @Transactional
    public void updateProjectTypeStatus(Long id, CommonStatus status) {
        // 1、检查服务项目类型是否存在
        checkProjectTypeExist(id);

        // 2、若是禁用，则需要禁用所有与之关联的服务项目
        if (status == CommonStatus.DISABLED) {
            this.update(new Project(), Wrappers.lambdaUpdate(Project.class)
                    .eq(Project::getProjectTypeId, id)
                    .set(Project::getStatus, CommonStatus.DISABLED));
        }

        // 3、更新
        projectTypeMapper.update(new ProjectType(), Wrappers.lambdaUpdate(ProjectType.class)
                .eq(ProjectType::getId, id)
                .set(ProjectType::getStatus, status));
    }

    /**
     * 根据服务项目类型id查询服务项目类型信息
     * @param id 服务项目类型id
     * @return 查询结果
     */
    @Override
    public ProjectType getProjectTypeById(Long id) {
        // 1、检查服务项目类型是否存在，并返回
        return checkProjectTypeExist(id);
    }

    /**
     * 修改服务项目类型信息
     * @param projectTypeUpdateDTO 修改的服务项目类型信息
     */
    @Override
    @Transactional
    public void updateProjectType(ProjectTypeUpdateDTO projectTypeUpdateDTO) {
        // 1、获取参数
        Long id = projectTypeUpdateDTO.getId();
        String projectTypeName = projectTypeUpdateDTO.getProjectTypeName();

        // 2、检查服务项目类型是否存在
        checkProjectTypeExist(id);

        // 3、更新
        projectTypeMapper.update(new ProjectType(), Wrappers.lambdaUpdate(ProjectType.class)
                .eq(ProjectType::getId, id)
                .set(ProjectType::getName, projectTypeName));
    }

    /**
     * 删除服务项目类型（逻辑删除）
     * @param id 服务项目类型id
     */
    @Override
    @Transactional
    public void deleteProjectType(Long id) {
        // 1、检查服务项目类型是否存在
        checkProjectTypeExist(id);

        // 2、检查服务项目类型是否关联有服务项目（未删除）
        long count = this.count(Wrappers.lambdaQuery(Project.class)
                .eq(Project::getProjectTypeId, id)
                .eq(Project::getDeleted, CommonDeleted.NOT_DELETED));
        if (count > 0) {
            // 服务项目类型关联有服务项目
            throw new DeletionNotAllowedException(MessageConstant.PROJECT_TYPE_BE_RELATED_BY_PROJECT + MessageConstant.CAN_NOT_DELETE);
        }

        // 3、删除服务项目类型（逻辑删除）
        projectTypeMapper.update(new ProjectType(), Wrappers.lambdaUpdate(ProjectType.class)
                .eq(ProjectType::getId, id)
                .set(ProjectType::getDeleted, CommonDeleted.DELETED));
    }

    /**
     * 查询所有启用的服务项目类型
     * @return 启用的服务项目类型列表
     */
    @Override
    public List<ProjectType> listProjectType() {
        return projectTypeMapper.selectList(Wrappers.lambdaQuery(ProjectType.class)
                .eq(ProjectType::getStatus, CommonStatus.ENABLED)
                .eq(ProjectType::getDeleted, CommonDeleted.NOT_DELETED));
    }

    /**
     * 查询所有服务项目
     * @return 服务项目信息列表
     */
    @Override
    public List<ProjectListVO> listProject() {
        // 1、查询所有启用的服务项目类型，按照id排序，并转换为id-name映射
        LinkedHashMap<Long, String> projectTypeIdNameMap = projectTypeMapper.selectList(Wrappers.lambdaQuery(ProjectType.class)
                        .eq(ProjectType::getStatus, CommonStatus.ENABLED)
                        .eq(ProjectType::getDeleted, CommonDeleted.NOT_DELETED)
                        .orderByAsc(ProjectType::getId))
                .stream()
                .collect(Collectors.toMap(ProjectType::getId, ProjectType::getName, (v1, v2) -> v1, LinkedHashMap::new));

        // 2、查询所有服务项目，按照id排序
        List<Project> projectList = this.list(Wrappers.lambdaQuery(Project.class)
                .eq(Project::getDeleted, CommonDeleted.NOT_DELETED)
                .orderByAsc(Project::getId));

        // 3、封装VO（一个服务项目类型对应一个VO，VO中包含多个服务项目）
        List<ProjectListVO> projectListVOList = new ArrayList<>();
        projectTypeIdNameMap.forEach((projectTypeId, projectTypeName) -> {
            ProjectListVO projectListVO = new ProjectListVO(projectTypeId, projectTypeName, projectList.stream()
                    .filter(project -> project.getProjectTypeId().equals(projectTypeId))
                    .collect(Collectors.toList()));
            projectListVOList.add(projectListVO);
        });

        // 4、返回封装结果
        return projectListVOList;
    }

    /**
     * 查询销量前十服务项目
     * @return 销量前十服务项目列表
     */
    @Override
    public List<ProjectSalesDTO> getProjectTop10() {
        return projectMapper.getProjectTop10(null, null);
    }

    /**
     * 构造服务项目物品关联信息列表
     * @param itemList 服务项目所管理物品信息列表
     * @param projectId 服务项目id
     * @return 服务项目物品关联信息列表
     */
    private List<ProjectItem> getProjectItemList(List<ProjectItemAddDTO> itemList, Long projectId) {
        // 1、检查物品是否存在
        List<Long> itemIds = itemList.stream().map(ProjectItemAddDTO::getItemId).toList();
        Long count = itemMapper.selectCount(Wrappers.lambdaQuery(Item.class)
                .in(Item::getId, itemIds)
                .eq(Item::getDeleted, CommonDeleted.NOT_DELETED));
        if (count < itemIds.size()) {
            // 有物品不存在，请先检查后添加，抛出异常
            throw new EntityNotFoundException(MessageConstant.ITEM_NOT_FOUND + MessageConstant.PLEASE_CHECK_BEFORE_ADD);
        }

        // 2、构造服务项目物品关联信息列表
        List<ProjectItem> projectItemList = new ArrayList<>();
        itemList.forEach(projectItemAddDTO -> {
            ProjectItem projectItem = new ProjectItem();
            BeanUtils.copyProperties(projectItemAddDTO, projectItem);
            projectItem.setProjectId(projectId);
            projectItemList.add(projectItem);
        });

        // 3、返回
        return projectItemList;
    }

    /**
     * 检查服务项目是否存在
     * @param projectId 服务项目id
     * @return 服务项目对象
     */
    private Project checkProjectExist(Long projectId) {
        Project project = this.getOne(Wrappers.lambdaQuery(Project.class)
                .eq(Project::getId, projectId)
                .eq(Project::getDeleted, CommonDeleted.NOT_DELETED));
        if (project == null) {
            // 服务项目不存在，抛出异常
            throw new EntityNotFoundException(MessageConstant.PROJECT_NOT_FOUND);
        }
        return project;
    }

    /**
     * 检查服务项目类型是否存在
     * @param projectTypeId 服务项目类型id
     * @return 服务项目类型对象
     */
    private ProjectType checkProjectTypeExist(Long projectTypeId) {
        ProjectType projectType = projectTypeMapper.selectOne(Wrappers.lambdaQuery(ProjectType.class)
                .eq(ProjectType::getId, projectTypeId)
                .eq(ProjectType::getDeleted, CommonDeleted.NOT_DELETED));
        if (projectType == null) {
            // 服务项目类型不存在，抛出异常
            throw new EntityNotFoundException(MessageConstant.PROJECT_TYPE_NOT_FOUND);
        }
        return projectType;
    }

}
